package cn.wizzer.app.user.modules.service.impl;

import cn.wizzer.app.user.modules.models.RedPacket;
import cn.wizzer.app.user.modules.models.RedPacketRecord;
import cn.wizzer.app.user.modules.models.enums.RedPacketTypeEnum;
import cn.wizzer.app.user.modules.models.enums.UserColEnum;
import cn.wizzer.app.user.modules.models.vo.RedPacketRecordVO;
import cn.wizzer.app.user.modules.services.RedPacketRecordService;
import cn.wizzer.app.user.modules.services.RedPacketService;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.base.service.BaseServiceImpl;
import cn.wizzer.framework.base.service.CommonDBService;
import cn.wizzer.framework.page.Pagination;
import cn.wizzer.framework.util.Id;
import cn.wizzer.framework.util.Result;
import com.alibaba.dubbo.config.annotation.Service;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@IocBean(args = {"refer:dao"})
@Service(interfaceClass = RedPacketRecordService.class)
public class RedPacketRecordServiceImpl extends BaseServiceImpl<RedPacketRecord> implements RedPacketRecordService {
    public RedPacketRecordServiceImpl(Dao dao) {
        super(dao);
    }


    @Inject
    private CommonDBService dbService;

    @Aop(TransAop.REPEATABLE_READ)
    @Override
    public void generate(RedPacket redPacket) {
        List<Double> algorithm = new ArrayList<>();
        // 根据红包类型生成红包记录
        if (RedPacketTypeEnum.RANDOM.equals(redPacket.getType())) {
            algorithm = RedPacketRecordServiceImpl.randomAlgorithm(Double.valueOf(redPacket.getTotal().toString()), redPacket.getQuantity());
        } else if (RedPacketTypeEnum.IDENTICAL.equals(redPacket.getType())) {
            algorithm = RedPacketRecordServiceImpl.identicalAlgorithm(Double.valueOf(redPacket.getTotal().toString()), redPacket.getQuantity());
        } else {
            Result.breakWith(BizExceptionEnum.RED_PACKET_TYPE_ERROR);
        }

        for (int i = 0; i < algorithm.size(); i++) {
            BigDecimal amount = new BigDecimal(algorithm.get(i).toString());
            RedPacketRecord redPacketRecord = new RedPacketRecord();
            redPacketRecord.setPacketNo(i);
            redPacketRecord.setNo(Id.createOrderNo());
            redPacketRecord.setCurrencyId(redPacket.getCurrencyId());
            redPacketRecord.setAmount(amount);
            redPacketRecord.setRedPacketId(redPacket.getId());
            this.insert(redPacketRecord);
        }
    }

    @Override
    public List<RedPacketRecordVO> listByAsc(String redPacketId) {
        StringBuffer sb = new StringBuffer();
        sb.append("select r.*,u.nickName ,u.headPicture,c.name as currencyName,c.logo as currencyLogo from red_packet_record r");
        sb.append(" left join  user u on u.id=r.userId");
        sb.append(" left join sys_currency c on c.id=r.currencyId");
        sb.append(" where r.redPacketId = '");
        sb.append(redPacketId);
        sb.append("' and r.userId is null order by r.no asc");
        return dbService.list(RedPacketRecordVO.class, sb.toString());
    }

    @Override
    public RedPacketRecordVO fetchByRedIdUserId(String redPacketId, String userId) {
        StringBuffer sb = new StringBuffer();
        sb.append("select r.*,u.nickName ,u.headPicture,c.name as currencyName,c.logo as currencyLogo from red_packet_record r");
        sb.append(" left join  user u on u.id=r.userId");
        sb.append(" left join sys_currency c on c.id=r.currencyId");
        sb.append(" where r.redPacketId = '");
        sb.append(redPacketId);
        sb.append("' and r.userId ='").append(userId).append("'");
        return dbService.fetchSql(RedPacketRecordVO.class, sb.toString());
    }

    /**
     * 要用户头像，币种LOGO,币种名称
     *
     * @param redPacketId
     * @return
     */
    @Override
    public List<RedPacketRecordVO> listByAscOpened(String redPacketId) {
        StringBuffer sb = new StringBuffer();
        sb.append("select r.*,u.nickName ,u.headPicture,c.name as currencyName,c.logo as currencyLogo from red_packet_record r");
        sb.append(" left join  user u on u.id=r.userId");
        sb.append(" left join sys_currency c on c.id=r.currencyId");
        sb.append(" where r.redPacketId = '");
        sb.append(redPacketId);
        sb.append("' and r.userId is not null order by r.no asc");
        return dbService.list(RedPacketRecordVO.class, sb.toString());
    }

    @Override
    public Pagination pageList(String userId, Integer pageNumber, int pageSize) {
        StringBuffer sb = new StringBuffer();
        sb.append("select r.*,u.nickName ,u.headPicture,c.name as currencyName,c.logo as currencyLogo from red_packet_record r\n");
        sb.append(" left join red_packet p on p.id=r.redPacketId");
        sb.append(" left join  user u on u.id=p.userId");
        sb.append(" left join sys_currency c on c.id=r.currencyId");
        sb.append(" where r.userId='").append(userId).append("'");
        sb.append(" order by r.openAt desc");
        return dbService.listPage(RedPacketRecordVO.class, pageNumber, pageSize, sb.toString());
    }

    @Override
    public Pagination listPage(String conditionData, String currencyId, String keyword, String[] searchDate, int pageNumber, int pageSize) {
        Sql sql = Sqls.create("SELECT pr.*,rp.no AS redPacketNo,u.nickName,u.userName,u.phone,sc.name AS currencyName FROM red_packet_record AS pr")
                .appendSourceSql("LEFT JOIN red_packet AS rp ON pr.redPacketId = rp.id")
                .appendSourceSql("LEFT JOIN user AS u ON pr.userId = u.id")
                .appendSourceSql("LEFT JOIN sys_currency AS sc ON pr.currencyId = sc.id $condition");
        Cnd cnd = Cnd.NEW();
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    cnd.and("u." + colName, "=", 0);
                }
            }
        }
        if (Strings.isNotBlank(currencyId)) {
            cnd.and("sc.id", "=", currencyId);
        }
        if (Strings.isNotBlank(keyword)) {
            keyword = "%" + keyword + "%";
            cnd.and(Cnd.exps("u.nickName", "LIKE", keyword)
                    .or("u.userName", "LIKE", keyword)
                    .or("u.phone", "LIKE", keyword)
                    .or("pr.no", "LIKE", keyword)
                    .or("rp.no", "LIKE", keyword)
            );
        }
        if (null != searchDate && searchDate.length != 0) {
            cnd.and("pr.ctAt", ">=", Times.d2TS(Times.D(searchDate[0])));
            cnd.and("pr.ctAt", "<=", Times.d2TS(Times.D(searchDate[1])));
        }
        sql.setCondition(cnd);
        sql.appendSourceSql("ORDER BY pr.openAt DESC");
        return dbService.listPage(RedPacketRecordVO.class, pageNumber, pageSize, sql.toString());
    }

    @Override
    public BigDecimal searchSum(String conditionData, String currencyId, String keyword, String[] searchDate) {
        StringBuffer sb = new StringBuffer();
        sb.append("select IFNULL(sum(r.amount * c.rate), 0) from red_packet_record r left join sys_currency c on r.currencyId=c.id ");
        sb.append(" left join user u on u.id = r.userId ");
        sb.append(" LEFT JOIN red_packet AS rp ON r.redPacketId = rp.id ");
        sb.append(" where 1=1");
        if (Strings.isNotBlank(conditionData)) {
            String[] split = conditionData.split(",");
            for (String col : split) {
                String colName = UserColEnum.getValByName(col);
                if (Strings.isNotBlank(colName)) {
                    // 默认不显示游客、内部账号
                    sb.append(" and u.").append(colName).append(" = 0");
                }
            }
        }
        if (Strings.isNotBlank(keyword)) {
            sb.append(" and ( u.nickName like '%").append(keyword).append("%'");
            sb.append(" or u.phone like '%").append(keyword).append("%'");
            sb.append(" or u.userName like '%").append(keyword).append("%' ");
            sb.append(" or rp.no like '%").append(keyword).append("%' ");
            sb.append(" or r.no like '%").append(keyword).append("%' )");
        }
        if (searchDate != null && searchDate.length > 0) {
            sb.append(" and r.ctAt >=").append(Times.d2TS(Times.D(searchDate[0])));
            sb.append(" and r.ctAt <=").append(Times.d2TS(Times.D(searchDate[1])));
        }
        if (Strings.isNotBlank(currencyId)) {
            sb.append(" and r.currencyId = '").append(currencyId).append("'");
        }

        Sql sql = Sqls.create(sb.toString());
        sql.setCallback(Sqls.callback.str());
        this.dao().execute(sql);
        return new BigDecimal(sql.getString()).setScale(6, BigDecimal.ROUND_DOWN);
    }

    /**
     * 随机红包
     *
     * @param moneySum
     * @param redNum
     * @return
     */
    private static List<Double> randomAlgorithm(double moneySum, int redNum) {
        List<Double> list = new ArrayList<Double>();
        if (redNum == 1) {
            list.add(moneySum);
            return list;
        }
        //精确小数点2位
        NumberFormat formatter = new DecimalFormat("#.##");
        // 设置最小的金额
        double moneyMin = 0.01;
        Random random = new Random();
        // 这个做判断条件
        double remainFlag = Double.valueOf(formatter.format(moneySum - redNum * moneyMin));
        // 这个做计算使用
        double remain = remainFlag;
        // 这个是分配掉的
        double used = 0;

        for (int i = 1; i < redNum; i++) {
            double money = 0;
            if (remainFlag == 0) {
                money = moneyMin;
            } else {
                // 随机计算出要分配的金额，这个要校验一下，如果不符合条件则给出最小的值就行
                money = Double.valueOf(formatter.format(random.nextDouble() * (remain/3.8 - moneyMin) + moneyMin));
                // 使用掉的+即将分配的 - 可用的 > 0
                if (used + money * 4 - remain + moneyMin * i > 0) {
                    money = moneyMin;
                }
            }
            // 使用掉累计
            used = used + money;
            list.add(money);
            //数值转换
            moneySum = Double.valueOf(formatter.format(moneySum - money));
        }
        if (remainFlag != 0) {
            list.add(moneySum);
        } else {
            list.add(moneyMin);
        }

        return list;
    }

    /**
     * 普通平均红包
     *
     * @param moneySum
     * @param redNum
     * @return
     */
    private static List<Double> identicalAlgorithm(double moneySum, int redNum) {
        List<Double> list = new ArrayList<Double>();
        //精确小数点2位
        NumberFormat formatter = new DecimalFormat("#.##");
        double money = moneySum / redNum;
        money = Double.valueOf(formatter.format(money));
        for (int i = 1; i < redNum; i++) {
            list.add(money);
            moneySum = moneySum - money;
        }
        list.add(Double.valueOf(formatter.format(moneySum)));
        return list;
    }

    public static void main(String[] args) {
        double moneySum = 2000;
        int redNum = 100;
//        System.out.println(RedPacketRecordServiceImpl.randomAlgorithm(moneySum, redNum));
//        System.out.println("==========");
//        System.out.println(RedPacketRecordServiceImpl.identicalAlgorithm(moneySum, redNum));

        List<Double> algorithm = RedPacketRecordServiceImpl.randomAlgorithm(moneySum, redNum);
        double x = 0;
        for (int i = 0; i < algorithm.size(); i++) {
            x = x + algorithm.get(i);
            System.out.println(algorithm.get(i));
//            System.out.println(algorithm.get(i));
//            BigDecimal amount = new BigDecimal(algorithm.get(i).toString());
//            System.out.println(amount);
//            System.out.println(amount.setScale(2, RoundingMode.DOWN));
        }
        System.out.println(algorithm.size());
        System.out.println(x);
    }


}
